// Copyright Gary Evans 2006-2007.

using System;
using System.Collections.Generic;
using System.Text;

using NUnit.Framework;

using Taumuon.Jabuka.Physics.Collision;

namespace Taumuon.Jabuka.Physics.Test
{
	/// <summary>
	/// Tests the collision response of two spheres
	///  where the collisions are directly along the
	///  line connecting the spheres' centres.
	///  The collisions are totally elastic, between
	///   rigid bodies.
	/// </summary>
	[TestFixture]
	public class TestSphereCollisionResponse1D
	{
		#region Tests

        /// <summary>
        /// Tests collision response between a moving
        ///  and stationary object.
        /// </summary>
		[Test]
		public void TestOneObjectStationary()
		{
			Sphere sphere1 = CreateSphere(1.0, 1.0, true);
			Sphere sphere2 = CreateSphere(1.0, 0.0, false);
            CollisionManager.DoCollisionResponse(sphere1, sphere2);
			Assert.IsTrue(MathUtils.IsEqualWithinTol(
				sphere1.EulerRigidBody.Velocity.X, 0.0, Tolerance));
			Assert.IsTrue(MathUtils.IsEqualWithinTol(
				sphere2.EulerRigidBody.Velocity.X, 1.0, Tolerance));
		}

        /// <summary>
        /// Tests the collision response of two spheres
        ///  moving towards each other with constant velocity.
        /// </summary>
		[Test]
		public void TestHeadOnCollision()
		{
			Sphere sphere1 = CreateSphere(1.0, 1.0, true);
			Sphere sphere2 = CreateSphere(1.0, -1.0, false);
            CollisionManager.DoCollisionResponse(sphere1, sphere2);
			Assert.IsTrue(MathUtils.IsEqualWithinTol(
				sphere1.EulerRigidBody.Velocity.X, -1.0, Tolerance));
			Assert.IsTrue(MathUtils.IsEqualWithinTol(
				sphere2.EulerRigidBody.Velocity.X, 1.0, Tolerance));
		}

        /// <summary>
        /// Tests collision response between a moving
        ///  and fixed stationary (infinite mass) object.
        /// </summary>
        [Test]
        public void TestImmovableObjectStationary()
        {
            Sphere sphere1 = CreateSphere(1.0, 1.0, true);
            Sphere sphere2 = CreateSphere(double.PositiveInfinity, 0.0, false);
            CollisionManager.DoCollisionResponse(sphere1, sphere2);
            Assert.IsTrue(MathUtils.IsEqualWithinTol(
                sphere1.EulerRigidBody.Velocity.X, -1.0, Tolerance));
            Assert.IsTrue(MathUtils.IsEqualWithinTol(
                sphere2.EulerRigidBody.Velocity.X, 0.0, Tolerance));
        }

        /// <summary>
        /// Tests collision response between a moving
        ///  and fixed stationary (infinite mass) object.
        /// </summary>
        /// <remarks>Change which object is fixed.</remarks>
        [Test]
        public void TestImmovableObjectStationary2()
        {
            Sphere sphere1 = CreateSphere(double.PositiveInfinity, 0.0, false);
            Sphere sphere2 = CreateSphere(1.0, -1.0, true);
            CollisionManager.DoCollisionResponse(sphere1, sphere2);
            Assert.IsTrue(MathUtils.IsEqualWithinTol(
                sphere1.EulerRigidBody.Velocity.X, 0.0, Tolerance));
            Assert.IsTrue(MathUtils.IsEqualWithinTol(
                sphere2.EulerRigidBody.Velocity.X, 1.0, Tolerance));
        }

		/// <summary>
		/// Tests a collision in the case where the stationary object is double
		///  the mass of the moving object.
		/// </summary>
		[Test]
		public void TestStationaryObjectDoubleMass()
		{
			Sphere sphere1 = CreateSphere(1.0, 1.0, true);
			Sphere sphere2 = CreateSphere(2.0, 0.0, false);
			CollisionManager.DoCollisionResponse(sphere1, sphere2);
			Assert.IsTrue(MathUtils.IsEqualWithinTol(
				sphere1.EulerRigidBody.Velocity.X, -1.0/3.0, Tolerance));
			Assert.IsTrue(MathUtils.IsEqualWithinTol(
				sphere2.EulerRigidBody.Velocity.X, 2.0/3.0, Tolerance));

			// before total mv = (1kg*1m/s) + (2kg*0m/s) = 1 kg m/s
			// before total Ek = ( 1/2 * (1)^2) + ( 1 * (0)^2) = 0.5J

			// after total mv = (1kg*(-1/3)) + (2*(2/3)) = -1/3 + 4/3 = 3/3 = 1 kg m/s
			// after total Ek = ( 1/2 * (-1/3)^2) + ( 1 * (2/3)^2 ) = ( 1/2 * 1/9 ) + (4/9) = 1/18 + 8/18 = 9/18 = 0.5J
		}

        /// <summary>
        /// Tests a case where the moving object is triple the mass
		///  of the stationary one.
        /// </summary>
		[Test]
		public void TestMovingObjectTripleMass()
		{
			Sphere sphere1 = CreateSphere(3.0, 1.0, true);
			Sphere sphere2 = CreateSphere(1.0, 0.0, false);
			CollisionManager.DoCollisionResponse(sphere1, sphere2);
			Assert.IsTrue(MathUtils.IsEqualWithinTol(
				sphere1.EulerRigidBody.Velocity.X, 0.5, Tolerance));
			Assert.IsTrue(MathUtils.IsEqualWithinTol(
				sphere2.EulerRigidBody.Velocity.X, 1.5, Tolerance));

			// before total mv = (3kg*1m/s) + (1kg*0m/s) = 3 kg m/s
			// before total Ek = ( 3/2 * (1)^2) + ( 1/2 * (0)^2) = 1.5J

			// after total mv = (3kg*0.5m/s) + (1kg*1.5m/s) = 3 kg m/s
			// after total Ek = ( 3/2 * (1/2)^2) + ((1/2) * (3/2)^2) = (3/2 * 1/4) + (1/2 * 9/4) = (3/8 + 9/8) = 12/8 = 1.5J
		}

		#endregion Tests

		#region Helper Methods

		/// <summary>
		/// Creates a Sphere with an EulerRigidBody, setting the mass and the
		///  initial velocity (only the x component).
		/// We're assuming that the spheres are touching so that we can
        ///  apply an impulse, and the collisions are along the line
		///  of the sphere's centres.
		/// </summary>
		/// <returns></returns>
		private Sphere CreateSphere(double mass, double initialVelocityX,
            bool isLeft)
		{
            EulerRigidBody body = new EulerRigidBody(new Vector(isLeft ? 0.0 : 2.0, 0.0, 0.0),
				new Vector(initialVelocityX, 0.0, 0.0), mass);
			return new Sphere(body, 0.0);
		}

		#endregion Helper Methods

		private const double Tolerance = 1e-10;
	}
}
